package com.framework.core.mybatis.ddl.pg;

import com.framework.core.mybatis.context.FieldInfo;
import com.framework.core.mybatis.context.TableContext;
import com.framework.core.mybatis.context.TableInfo;
import com.framework.core.mybatis.ddl.AbstractMybatisDdlFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 *  主要执行类-pg支持
 *  created by zzg
 *  2024-12-06
 */
public class PostgresqlDdlFactory extends AbstractMybatisDdlFactory {
    @Override
    public void execute(TableContext context) {
        super.generalStatements(context);
    }

    @Override
    protected List<String> createTableStatements(List<TableInfo> tableInfoList) {
        List<String> statements = new ArrayList<>();
        for (TableInfo it : tableInfoList) {
            // comment语句
            StringBuilder commentStatement = new StringBuilder();
            StringBuilder createStatement = new StringBuilder("CREATE TABLE ");
            createStatement.append(it.getTableName()).append(" (\n");
            List<FieldInfo> fieldInfoList = it.getFieldInfoList();
            createSql(fieldInfoList, it.getTableName(), createStatement, commentStatement);
            createStatement.append(");");
            statements.add(createStatement.toString());
        }
        return statements;
    }

    @Override
    protected List<String> updateTableStatements(List<TableInfo> existsList) {
        List<String> statements = new ArrayList<>();
        for (TableInfo it : existsList) {
            // 数据库字段
            List<String> selectColumns = selectColumns(it.getTableName());
            // 实体类字段
            List<FieldInfo> fieldInfoList = it.getFieldInfoList();
            boolean hasChange = false;
            if (selectColumns.size() != fieldInfoList.size()) {
                // 判断是否缺失
                hasChange = true;
            }
            if (!hasChange) {
                continue;
            }
            // 过滤掉已有的
            fieldInfoList = fieldInfoList.stream().filter(fieldInfo -> !selectColumns.contains(fieldInfo.getValue()))
                    .collect(Collectors.toList());
            // 没有新增
            if (ObjectUtils.isEmpty(fieldInfoList)) {
                continue;
            }
            // comment语句
            StringBuilder commentStatement = new StringBuilder();
            // 创建语句
            StringBuilder alterStatement = new StringBuilder("ALTER TABLE ");
            alterStatement.append(it.getTableName()).append(" ADD(\n");
            createSql(fieldInfoList, it.getTableName(), alterStatement, commentStatement);
            alterStatement.append(");");
            statements.add(alterStatement.toString());
            statements.add(commentStatement.toString());
        }
        return statements;
    }

    private void createSql(List<FieldInfo> fieldInfoList, String tableName, StringBuilder statement, StringBuilder commentStatement) {
        for (int i = 0; i < fieldInfoList.size(); i++) {
            FieldInfo fieldInfo = fieldInfoList.get(i);
            addColumn(fieldInfo, statement);

            if (i < fieldInfoList.size() - 1) {
                statement.append(",\n");
            }

            if (StringUtils.isNotBlank(fieldInfo.getComment())) {
                commentStatement.append("COMMENT ON COLUMN ");
                commentStatement.append(tableName);
                commentStatement.append(".");
                commentStatement.append(fieldInfo.getValue());
                commentStatement.append(" IS ");
                commentStatement.append(fieldInfo.getComment());
                commentStatement.append(";\n");
            }
        }
    }

    private void addColumn(FieldInfo fieldInfo, StringBuilder statement) {
        String property = fieldInfo.getProperty();
        if (ObjectUtils.isEmpty(property)) {
            property = getPropertyByFieldType(fieldInfo);
        }
        statement
                .append(fieldInfo.getValue())
                .append(" ")
                .append(property);

        if (fieldInfo.isPrimaryKey()) {
            statement.append(" PRIMARY KEY");
        }
    }

    private String getPropertyByFieldType(FieldInfo fieldInfo) {
        boolean primaryKey = fieldInfo.isPrimaryKey();
        if (primaryKey) {
            return " varchar(32) NOT NULL";
        }
        String result = " varchar(255) DEFAULT ''";
        JdbcType jdbcType = fieldInfo.getJdbcType();
        switch (jdbcType) {
            case TIMESTAMP:
                result = " timestamp without time zone DEFAULT NULL";
                break;
            case DATE:
                result = " date DEFAULT NULL";
                break;
            case TIME:
                result = " time without time zone DEFAULT NULL";
                break;
            case NUMERIC:
                result = " numeric(20,2) DEFAULT 0";
                break;
            case INTEGER:
                result = " integer DEFAULT 0";
                break;
            case BIGINT:
                result = " bigint DEFAULT 0";
                break;
            case NVARCHAR:
            case VARCHAR:
                result = " varchar(255) DEFAULT ''";
                break;
            case NCHAR:
            case CHAR:
                result = " char(1) DEFAULT ''";
                break;
            case BOOLEAN:
                result = " boolean DEFAULT false";
                break;
            case FLOAT:
                result = " real DEFAULT 0";
                break;
            case DOUBLE:
                result = " double precision DEFAULT 0";
                break;
            case TINYINT:
            case SMALLINT:
                result = " smallint DEFAULT 0";
                break;
            case BLOB:
                result = " bytea";
                break;
            case CLOB:
                result = " text";
                break;
        }

        return result;
    }
}
